///////////////////////////////////////////////////////////////////////////////////////////////////
//
// PROJECT : ARIRANG family(ARIRANG-224,ARIRANG-256,ARIRANG-384,ARIRANG-512)
//
// DATE    : 2008.10.23
//
///////////////////////////////////////////////////////////////////////////////////////////////////
//
// FILE  : SHA3api_OP32.c
//
// NOTES : Optimized code of ARIRANG family
//
//         Based on 32-bit platform (with four 32-bit S-box and sixteen 32-bit S-box version)
//
///////////////////////////////////////////////////////////////////////////////////////////////////

#include <stdio.h>
#include <memory.h>
#include "Arirang_OP32.h"
#include "SHA3api_OP32.h"


///////////////////////////////////////////////////////////////////////////////////////////////////
//
// FUNCTION NAME  : Init
//
// DESCRIPTION    : Initialize a hashState with the intended hash length of particular instantiation.
//
// PARAMETERS     : state - a structure that holds the hashState information
//                  hashbitlen - an integer value that indicates the length of the hash output in bits
//
// RETURNS        : SUCCESS - on success
//                  BAD_HASHLEN - hashbitlen is invalid
//
///////////////////////////////////////////////////////////////////////////////////////////////////

HashReturn Init(hashState *state, int hashbitlen)
{
	if ((hashbitlen != 224) && (hashbitlen != 256) && (hashbitlen != 384) && (hashbitlen != 512))
		return BAD_HASHLEN;

	gen_tabs();
	// Setting the Hash Length
	state->hashbitlen = hashbitlen;

	// Setting the Counter Values
	state->counter[0] = state->counter[1] = state->counter[2] = state->counter[3]= 0;

	// Initialize the Data Length
	state->count[0] = state->count[1] = state->count[2] = state->count[3] = 0;

	// Setting the Initial Hash Value
	if(state->hashbitlen == 224){
		state->workingvar[0] = 0xcbbb9d5d;
		state->workingvar[1] = 0x629a292a;
		state->workingvar[2] = 0x9159015a;
		state->workingvar[3] = 0x152fecd8;
		state->workingvar[4] = 0x67332667;
		state->workingvar[5] = 0x8eb44a87;
		state->workingvar[6] = 0xdb0c2e0d;
		state->workingvar[7] = 0x47b5481d;
		state->blocklen=ARIRANG256_BLOCK_LEN;
	}

	else if(state->hashbitlen == 256){
		state->workingvar[0] = 0x6a09e667;
		state->workingvar[1] = 0xbb67ae85;
		state->workingvar[2] = 0x3c6ef372;
		state->workingvar[3] = 0xa54ff53a;
		state->workingvar[4] = 0x510e527f;
		state->workingvar[5] = 0x9b05688c;
		state->workingvar[6] = 0x1f83d9ab;
		state->workingvar[7] = 0x5be0cd19;
		state->blocklen=ARIRANG256_BLOCK_LEN;

	}
	else if(hashbitlen == 384){
		state->workingvar[ 0]=0xcbbb9d5d; state->workingvar[ 1]=0xc1059ed8;
		state->workingvar[ 2]=0x629a292a; state->workingvar[ 3]=0x367cd507;
		state->workingvar[ 4]=0x9159015a; state->workingvar[ 5]=0x3070dd17;
		state->workingvar[ 6]=0x152fecd8; state->workingvar[ 7]=0xf70e5939;
		state->workingvar[ 8]=0x67332667; state->workingvar[ 9]=0xffc00b31;
		state->workingvar[10]=0x8eb44a87; state->workingvar[11]=0x68581511;
		state->workingvar[12]=0xdb0c2e0d; state->workingvar[13]=0x64f98fa7;
		state->workingvar[14]=0x47b5481d; state->workingvar[15]=0xbefa4fa4;
		state->blocklen=ARIRANG512_BLOCK_LEN;

	}
	else if(hashbitlen == 512){
		state->workingvar[ 0]=0x6a09e667; state->workingvar[ 1]=0xf3bcc908;
		state->workingvar[ 2]=0xbb67ae85; state->workingvar[ 3]=0x84caa73b;
		state->workingvar[ 4]=0x3c6ef372; state->workingvar[ 5]=0xfe94f82b;
		state->workingvar[ 6]=0xa54ff53a; state->workingvar[ 7]=0x5f1d36f1;
		state->workingvar[ 8]=0x510e527f; state->workingvar[ 9]=0xade682d1;
		state->workingvar[10]=0x9b05688c; state->workingvar[11]=0x2b3e6c1f;
		state->workingvar[12]=0x1f83d9ab; state->workingvar[13]=0xfb41bd6b;
		state->workingvar[14]=0x5be0cd19; state->workingvar[15]=0x137e2179;
		state->blocklen=ARIRANG512_BLOCK_LEN;
	}
	return SUCCESS;
}

///////////////////////////////////////////////////////////////////////////////////////////////////
//
// FUNCTION NAME  : Update
//
// DESCRIPTION    : Process the supplied data.
//
// PARAMETERS     : state - a structure that holds the hashState information
//                  data - the data to be hashed
//                  Databitlen - the length, in bits, of the data to be hashed
//
// RETURNS        : SUCCESS - on success
//
///////////////////////////////////////////////////////////////////////////////////////////////////

HashReturn Update(hashState *state, const BitSequence *data, DataLength databitlen)
{
	DWORD RemainedLen, PartLen, temp;
	DWORD databytelen;

	state->remainderbit = databitlen & 7;

	databytelen = ( databitlen >> 3) + (state->remainderbit != 0);

	// Compute the number of hashed bytes mod ARIRANG_BLOCK_LEN
	RemainedLen = (state->count[0] >> 3) % state->blocklen;

	// Compute the number of bytes that can be filled up
	PartLen = state->blocklen - RemainedLen;

	// Update count (number of toatl data bits)
	temp = state->count[0] + (databytelen << 3);
	if( temp  < state->count[0] ){
		state->count[ 1]++; if(state->count[ 1] != 0x00);
		else {state->count[ 2]++; if(state->count[ 2] != 0x00);
		else {state->count[ 3]++;}}
	}
	state->count[0]=temp;

	temp = state->count[1] + (databytelen >> 29);
	if( temp  < state->count[1] ){
		state->count[ 2]++; if(state->count[ 1] != 0x00);
		else {state->count[ 3]++;}
	}
	state->count[1]=temp;

	if ((databytelen > PartLen) || ((databytelen == PartLen) && (state->remainderbit == 0)) ) {
		memcpy(state->block + RemainedLen, data, (int)PartLen);
		if(state->hashbitlen <257) Arirang_Compression256(state);
		else Arirang_Compression512(state);

		data += PartLen;
		databytelen -= PartLen;
		RemainedLen = 0;

		while( (databytelen > state->blocklen) || ((databytelen == state->blocklen) && (state->remainderbit == 0)) ) {
			memcpy((BYTE *)state->block, data, (int)state->blocklen);
			if(state->hashbitlen <257) Arirang_Compression256(state);
			else Arirang_Compression512(state);

			data += state->blocklen;
			databytelen -= state->blocklen;
		}
	}

	//	Buffer remaining input
	memcpy((BYTE *)state->block + RemainedLen, data, (int)databytelen);

	return SUCCESS;
}


///////////////////////////////////////////////////////////////////////////////////////////////////
//
// FUNCTION NAME  : Final
//
// DESCRIPTION    : Perform any post processing and ouput filtering required and return the final
//                  hash value.
//
// PARAMETERS     : state - a structure that holds the hashState information
//                  hashval - the storage for the final hash value to be returned
//
// RETURNS        : SUCCESS - on success
//
///////////////////////////////////////////////////////////////////////////////////////////////////

HashReturn Final(hashState *state, BYTE *hashval)
{
	DWORD i, dwIndex, Count[4];
	DWORD temp=(state->blocklen >> 3);

	// Padding the message
	if(state->remainderbit){
		Count[0] = state->count[0] + state->remainderbit - 8;
		Count[1] = state->count[1];
		Count[2] = state->count[2];
		Count[3] = state->count[3];

		dwIndex = ((Count[0] + (state->blocklen<<3) ) >> 3) % state->blocklen;
		state->block[dwIndex] &= 0xff-(1<<(8-state->remainderbit))+1;
		state->block[dwIndex++] ^= 0x80>>(state->remainderbit);
	}
	else{
		Count[0] = state->count[0];
		Count[1] = state->count[1];
		Count[2] = state->count[2];
		Count[3] = state->count[3];
		dwIndex = (Count[0] >> 3) % state->blocklen;
		state->block[dwIndex++] = 0x80;
	}

	if (dwIndex > (state->blocklen - temp)){
		memset((BYTE *)state->block + dwIndex, 0, (int)(state->blocklen - dwIndex));
		if(state->hashbitlen <257) Arirang_Compression256(state);
		else Arirang_Compression512(state);

		memset((BYTE *)state->block, 0, (int)state->blocklen - temp);
	}
	else
		memset((BYTE *)state->block + dwIndex, 0, (int)(state->blocklen - dwIndex - temp));

#if defined(LITTLE_ENDIAN)
	Count[0] = ENDIAN_REVERSE_DWORD(Count[0]);
	Count[1] = ENDIAN_REVERSE_DWORD(Count[1]);
	Count[2] = ENDIAN_REVERSE_DWORD(Count[2]);
	Count[3] = ENDIAN_REVERSE_DWORD(Count[3]);
#endif

	// Fixed counter value for the last message block
	if(state->hashbitlen > 257){
		((DWORD *)state->block)[state->blocklen/4-4] = Count[3];
		((DWORD *)state->block)[state->blocklen/4-3] = Count[2];
		((DWORD *)state->block)[state->blocklen/4-2] = Count[1];
		((DWORD *)state->block)[state->blocklen/4-1] = Count[0];
		state->counter[3]=0xb7e15162;	state->counter[2]=0x8aed2a6a;
		state->counter[1]=0xbf715880;	state->counter[0]=0x9cf4f3c7;
	}
	else{
		((DWORD *)state->block)[state->blocklen/4-2] = Count[1];
		((DWORD *)state->block)[state->blocklen/4-1] = Count[0];
		state->counter[1]=0xb7e15162;	state->counter[0]=0x8aed2a6a;
	}

	if(state->hashbitlen <257) Arirang_Compression256(state);
	else Arirang_Compression512(state);

	for (i = 0; i < (state->hashbitlen >> 3); i += 4)
		BIG_D2B((state->workingvar)[i / 4], &(hashval[i]));

	return SUCCESS;
}

///////////////////////////////////////////////////////////////////////////////////////////////////
//
// FUNCTION NAME  : Hash
//
// DESCRIPTION    : Hash the supplied data and provide the resulting hash value.
//
//
// PARAMETERS     : state      - a structure that holds the hashState information
//                  data       - the data to be hashed
//                  databitlen - the length, in bits, of the data to be hashed
//                  hashval    - the storage for the final hash value to be returned
//
// RETURNS        : SUCCESS - on success
//
///////////////////////////////////////////////////////////////////////////////////////////////////
HashReturn Hash(int hashbitlen, const BitSequence *data, DataLength databitlen, BitSequence *hashval){

	hashState State;

	Init(&State, hashbitlen);

	Update(&State, data, databitlen);

	Final(&State, hashval);

	return SUCCESS;
}
